/**
 * Dragger
 *
 * @author		Allen Chou
 * @version		1.0.2 (last update: Aug 7 2008)
 * @link		http://cjcat2266.deviantart.com
 */

package idv.cjcat.utils.dragger {
	import flash.display.*;
	import flash.events.*;
	import flash.errors.IllegalOperationError;
	
	import idv.cjcat.*;
	import idv.cjcat.events.DraggerEvent;
	import idv.cjcat.geom.Vector2;
	
	/**
	 * This class is for
	 */
	public class Dragger extends EventDispatcher{
		
		public var direction:Vector2;
		
		private var _dragArea:DisplayObject;
		
		private static var _stage:Stage = null;
		
		private var _draggees:Array = new Array();
		private var _x:Number;
		private var _y:Number;
		
		private var _dragging:Boolean = false;
		
		public static function init(stage:Stage):void {
			_stage = stage;
		}
		
		public function Dragger(dragArea:DisplayObject = null, direction:Vector2 = null):void {
			if (_stage == null) throw new IllegalOperationError("Call Dragger.init(stage) first.");
			_stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
			_stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
			
			this.dragArea = dragArea;
			
			if (direction != null) {
				this.direction = direction;
			} else {
				this.direction = new Vector2(1, 0);
			}
		}
		
		public function add(draggee:Draggee):void {
			_draggees.push(draggee);
		}
		
		public function remove(draggee:Draggee):void {
			for (var i:int = 0; i < _draggees.length; i++) {
				if (_draggees[i] === draggee) {
					_draggees.splice(i, 1);
					if (_draggees.length == 0) {
						_stage.removeEventListener(MouseEvent.MOUSE_MOVE, drag);
					}
					return;
				}
			}
		}
		
		public function removeAll():void {
			for (var i:int = 0; i < _draggees.length; i++) {
				remove(_draggees[i]);
			}
		}
		
		private var temp:Number;
		private var temp2:Number;
		private var temp3:Number;
		private var tempD:Draggee;
		private var tempV:Vector2 = new Vector2();
		private var exceed:Boolean = false;
		private function drag(e:MouseEvent):void {
				tempV.x = _stage.mouseX - _x;
				tempV.y = _stage.mouseY - _y;
				
			for (var i:int = 0; i < _draggees.length; i++) {
				tempD = _draggees[i];
				if (!tempD.active) continue;
				
				if (!tempD.useAngle) {
					temp = tempV.dot(direction.unitVec()) * tempD.factor + tempD._value;
					tempD._value = CJMath.clamp(temp, tempD._min, tempD._max);
				} else {
					temp2 = Math.atan2(_stage.mouseY - _dragArea.y, _stage.mouseX - _dragArea.x);
					temp3 = Math.atan2(_y - _dragArea.y, _x - _dragArea.x);
					
					while (temp2 - temp3 >= Math.PI) temp3 += Math.PI * 2;
					while (temp3 - temp2 >= Math.PI) temp2 += Math.PI * 2;
					
					temp = (temp2 - temp3) / Math.PI * 180 * tempD.factor + tempD._value;
					tempD._value = CJMath.clamp(temp, tempD._min, tempD._max);
				}
				
				tempD.obj[tempD.property] = tempD._value;
				tempD.dispatchEvent(new DraggerEvent(DraggerEvent.CHANGE, this, tempD, tempD.property, temp, tempD._value));
			}
			_x = _stage.mouseX;
			_y = _stage.mouseY;
			dispatchEvent(new Event(Event.CHANGE));
		}
		
		private function mouseDown(e:MouseEvent):void {
			if ((_draggees.length > 0) && ((_dragArea == _stage) || (_dragArea.hitTestPoint(_stage.mouseX, _stage.mouseY)))) {
				_dragging = true;
				for (var i:int = 0; i < _draggees.length; i++) {
					tempD = _draggees[i];
					tempD.dispatchEvent(new DraggerEvent(DraggerEvent.START, this, tempD, tempD.property, tempD.obj[tempD.property], tempD._value));
				}
				_x = _stage.mouseX;
				_y = _stage.mouseY;
				_stage.addEventListener(MouseEvent.MOUSE_MOVE, drag);
			}
		}
		
		private function mouseUp(e:MouseEvent):void {
			_stage.removeEventListener(MouseEvent.MOUSE_MOVE, drag);
			
			if (_dragging) {
				_dragging = false;
				for (var i:int = 0; i < _draggees.length; i++) {
					tempD = _draggees[i];
					tempD.dispatchEvent(new DraggerEvent(DraggerEvent.STOP, this, tempD, tempD.property, tempD.obj[tempD.property], tempD._value));
				}
			}
			
		}
		
		public function get dragArea():DisplayObject { return _dragArea; }
		public function set dragArea(value:DisplayObject):void {
			if (value == null) throw new IllegalOperationError("The dragArea property must not be null.");
			_dragArea = value;
		}
	}
}